<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2018/6/16
 * Time: 21:00
 */
namespace Pay\Controller;

use Common\Common\Api\Order;
use Common\Common\Api\Ordermember;
use Common\Common\Api\Redis\Channel;
use Common\Common\Api\Redis\Product;

/**
 * Class ManyiController
 * @package Pay\Controller
 * Ds ajie
 */
class DsController extends PayController{
    protected $config;
    protected $class;

    // 签名证书
    protected $merchantPrivateKeyPath= 'Files/Ds/merchant.pfx';


    //注意：公钥的格式一定要遵循如下：begin开头，end结尾，每行64字节，头尾不能有空格或回车
    protected $easyPublicKey = '-----BEGIN PUBLIC KEY-----
MIGeMA0GCSqGSIb3DQEBAQUAA4GMADCBiAKBgGiYdXXRFuYDIG5Q8wKc49Bj4kz4J/2DMRN7NIipZt1GxeYGx3hCPAVRv45fFTTvd50WKQQK1fbBABec3MUIghTvJ+r9bulaxaMPkNM69DKMTMFun5bwtkTUmfx+X1T1+9/l2LKc+JC9ux3vxYYkl0iz/Kd4qrol6FRM79GV/QrLAgMBAAE=
-----END PUBLIC KEY-----';

    protected $merchantPrivateKeyPwd = '11111111';
    protected $merchantPublicKey = '-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCiKnK8RXQ8UHDkMJ8Dq1jT2n0FHtvk+5oo/4XhlvM8lURBFAYqFi9q1gJ/tT2go7HOVSLuVxXKCdS+TR75olC2i3/QFCxm1PtUO4tsG55fvn/zkyGG8zf2m8f2na1T9VJ6IS99ezVsrUGlKO5fC84f9w61STzSRQvlG/+bmAO/sQIDAQAB
-----END PUBLIC KEY-----';


    //需要签名的请求字段，按顺序
    protected $reqSignFields = array(
            'serviceCode',
            'processCode',
            'merchantNo',
            'merchantOrderNo',
            'sequenceNo',
            'direction',
            'orderNo',
            'orderDate',
            'amount',
            'refundAmount',
            'currency',
            'accountNo'
        );

    //需要签名的返回字段，按顺序
    protected $resSignFields = array(
            'serviceCode',
            'processCode',
            'merchantNo',
            'merchantOrderNo',
            'sequenceNo',
            'orderNo',
            'amount',
            'refundAmount',
            'settleDate',
            'reckonSeqNo',
            'orderState',
            'responseCode'
        );


    public function __construct(){
        parent::__construct();
        $classes = explode("\\",__CLASS__);
        $class = end($classes);
        $this->class = substr($class, 0, stripos($class, "Controller"));
    }

    public function pay($array){
        $this->config = Channel::getInstance()->getById($array['channel']);

        if(!$this->config){
            $this->showmessage('通道错误!',['code'=>C("THIRDCODES.".$this->class)]);
        }

        $parameter = array(
            'code' => $this->config['code'], // 通道名称
            'title' => $this->config['title'],
            'exchange' => 1, // 金额比例
            'gateway' => $this->config['gateway'],
            'orderid' => createOrderId(),
            'out_trade_id' => I("request.ordNo") ? I("request.ordNo") : I("request.pay_orderid"),
            'body' => 'pay',
            'channel'=>$array
        );

        // $isMobile = isMobile();
        $return = $this->orderadd( $parameter );

        switch($this->config['paytype']){
            //银联扫码
            case 11:
             //银联快捷
            case 13:
                $pay_bankcode = '5';
                break;
            default:
                $this->showmessage('暂不支持此方式!',['code'=>C("THIRDCODES.".$this->class)]);
        }

        $bean = array(
            'serviceCode'       => '0200',
            'processCode'       => '190031',
            'merchantNo'        => $this->config['mch_id'],
            'merchantOrderNo'   => $return['orderid'],
            'sequenceNo'        => $return['orderid'],
            'amount'            => strval(number_format($return['amount'],2,'.','')),
            'currency'          => '156',
            'description'       => urlencode('快捷支付'),
            'asynResUrl1'       => urlencode($return['notifyurl']),
            'asynResUrl2'       => urlencode($return['notifyurl']),
            'synResUrl'         => urlencode($return['callbackurl']),
        );

        $bean['signMsg'] = urlencode($this->sign1($bean, $this->reqSignFields,$this->merchantPrivateKeyPath,$this->merchantPrivateKeyPwd));

            $json = urldecode(json_encode($bean, JSON_UNESCAPED_UNICODE));//由于上面有些字段用了encode，所以这里需要decode还原(为了解决json格式化转码问题)
        $requestStr = $this->encrypt1($json,$this->easyPublicKey);

       // var_dump('<pre/>',$bean,'参数：'.$json,'请求串：'.$requestStr,'测试网关：'.$this->config['gateway'],$this->config['signkey']);exit();

        $str = "<html><head></head><body>";
        $str.= "<form id='pay_form' name='pay_form' action='".$this->config['gateway']."' method='POST'>\n";
        $str.= "<input type='hidden' name='".$requestStr."'>\n";
        $str.= "</form>\n";
        $str.= "<script language='javascript'>window.onload=function(){document.pay_form.submit();}</script>\n";
        $str.= "</body></html>";

        echo $str;
        exit();


    }


    protected function sign1($bean,$signFileds,$pfxPath,$pfxPwd) {

        $beSignMsg = $this->toSignString1($bean,$signFileds);


        return $this->sign2($beSignMsg,$pfxPath,$pfxPwd);
    }

    protected function toSignString1($bean,$signFileds) {

        $signString = '';

        foreach ($signFileds as $key=>$val) {

            $bVal = $this->getValue($bean,$val);

            if(!empty($bVal)) {
                $signString = $signString.$bVal.' ';
            }
        }

        $signString = substr($signString,0,strlen($signString)-1);

        return $signString;
    }

    protected function getValue($arr, $key) {
        foreach($arr as $k => $v) {
            if ($k == $key) {
                return $v;
            }
        }
        return '';
    }

    static function sign2($beSignMsg, $pfxPath, $pfxPwd) {

        $certs = array();
        openssl_pkcs12_read(file_get_contents($pfxPath), $certs, $pfxPwd);
       // var_dump($certs);exit();
        if(!$certs) return ;

        $signature = '';
        openssl_sign($beSignMsg, $signature, $certs['pkey'], "SHA1");

        return base64_encode($signature);
    }


    protected function encrypt1($json,$easyPublicKey) {
        $des3Key = base64_encode($this->randomHex(24));
        $des3EncryptString = $this->encrypt2($des3Key,$json);
        $des3KeyRsaEncryptedString = $this->encrypt3($easyPublicKey,$des3Key);

        return $des3KeyRsaEncryptedString.'|'.$des3EncryptString;
    }

    protected function randomHex($length){
        $str = null;
        $strPol = "0123456789ABCDEF";
        $max = strlen($strPol)-1;

        for($i=0;$i<$length;$i++){
            $str.=$strPol[rand(0,$max)];
        }

        return $str;
    }

    public function encrypt2($key,$input) { // 数据加密

        $key = base64_decode($key);

        $size = mcrypt_get_block_size ( MCRYPT_3DES, 'ecb' );
        $input = $this->pkcs5_pad1 ( $input, $size );
        $key = str_pad ( $key, 24, '0' );
        $td = mcrypt_module_open ( MCRYPT_3DES, '', 'ecb', '' );
        $iv = @mcrypt_create_iv ( mcrypt_enc_get_iv_size ( $td ), MCRYPT_RAND );
        @mcrypt_generic_init ( $td, $key, $iv );
        $data = mcrypt_generic ( $td, $input );
        mcrypt_generic_deinit ( $td );
        mcrypt_module_close ( $td );
        $data = base64_encode ( $data );
        return $data;
    }

    public function pkcs5_pad1($text, $blocksize) {
        $pad = $blocksize - (strlen ( $text ) % $blocksize);
        return $text . str_repeat ( chr ( $pad ), $pad );
    }

    public function encrypt3($publicKey, $data) {

        $key = openssl_get_publickey($publicKey);

        $original_arr = str_split($data,117);
        foreach($original_arr as $o) {
            $sub_enc = null;
            openssl_public_encrypt($o,$sub_enc,$key);
            $original_enc_arr[] = $sub_enc;
        }

        openssl_free_key($key);

        $original_enc_str = base64_encode(implode('',$original_enc_arr));

        return $original_enc_str;
    }

    protected function TripleDesdecrypt($key,$encrypted) {

        $encrypted = base64_decode ( $encrypted );
        $key = str_pad ( $key, 24, '0' );
        $td = mcrypt_module_open ( MCRYPT_3DES, '', 'ecb', '' );
        $iv = @mcrypt_create_iv ( mcrypt_enc_get_iv_size ( $td ), MCRYPT_RAND );
        $ks = mcrypt_enc_get_key_size ( $td );
        @mcrypt_generic_init ( $td, $key, $iv );
        $decrypted = mdecrypt_generic ( $td, $encrypted );
        mcrypt_generic_deinit ( $td );
        mcrypt_module_close ( $td );
        $y = $this->pkcs5_unpad( $decrypted );
        return $y;
    }

    protected function pkcs5_unpad($text) {
        $pad = ord ( $text {strlen ( $text ) - 1} );
        if ($pad > strlen ( $text )) {
            return false;
        }
        if (strspn ( $text, chr ( $pad ), strlen ( $text ) - $pad ) != $pad) {
            return false;
        }
        return substr ( $text, 0, - 1 * $pad );
    }

    protected function Rsadecrypt($data, $pfxPath, $pfxPwd) {

        $certs = array();
        openssl_pkcs12_read(file_get_contents($pfxPath), $certs, $pfxPwd);
        if(!$certs) return ;

        $decrypt = '';
        openssl_private_decrypt(base64_decode($data), $decrypt, $certs['pkey']);
        return base64_decode($decrypt);
    }




    protected function parseResponse1($response, $signFields, $myPfxPath, $myPfxPwd, $easyPublicKey) {

        $tmp = explode("|", $response);

        $des3KeyRsaEncryptedString = $tmp[0];
        $des3EncryptString = $tmp[1];

        $des3Key = $this->Rsadecrypt($des3KeyRsaEncryptedString, $myPfxPath, $myPfxPwd);

        $json = $this->TripleDesdecrypt($des3Key, $des3EncryptString);

        $bean = json_decode($json,true);

        return $bean;
    }


    protected function verify1($bean, $signFields, $signMsg, $publicKey) {

        $string = $this->toSignString1($bean,$signFields);

        return $this->Rsaverify($signMsg, $publicKey, $string);
    }

    protected function Rsaverify($signMsg, $publicKey, $data) {

        $key = openssl_get_publickey($publicKey);
        $sign = base64_decode($signMsg);

        $result = (bool)openssl_verify($data, $sign, $key, OPENSSL_ALGO_SHA1);

        openssl_free_key($key);

        return $result;
    }


    //通知
    public function notifyurl(){
        //$this->debug_callback();

        //IP白名单
        if(!$this->checkip(getIP(),['119.29.223.43'])){
           // exit("wrong");
        }

        $resp = file_get_contents('php://input', 'r');

       // $resp =  'fFqtC0AFeAKDObXq6NHA+KHu7GMHRKKOR88griIZxHn1bGlb2fizWPv3RB9v8vq7cPt1KXWDpeYtS3GwRJlU+q1Ck/tnLsHMy1Nzq5M+YRml+KD+2LmRVDM9AV24FNTOIl1ifUId0NXVVVlaZRhAAGmXbSVhbdOlJaVngHAJ7nk=|y7KM9djWVbKw9vcU6JGiQF7h0OxhpzDIETHxngKe9rM74yWMm7WACkwBsrJoWzRz9eO+D4eo7DJQYVoFHFCVeO2nOCglmeOjFBfVOoMsQzcZL/mhtnXLWCBEvV/nPSasAhs4WYrMp8N+jqpuBjkdPCGRZAR0E5z939jXu64xNyhwgp1LLxXsbCARPUc80aNnRQjlDQf95xZxMMubnjGhv1SZYtNzkHb/kMlaisLDOyoFsBZoyFWzYPw34m3IZsDjHYdGCkphJ6MidFJ2+/UpmTRGB1TWvk+9wat7imf6bzr7okO3I4ZQqh39DsD555es8DsneP71uqx+83kK6AmjoJJcGDAM0rNdrRTZ7AT6LExq/EAD/1FaSbD29xTokaJAe1a4NxpQ8Ce+9sJFmWNtL+LdiipuJyC7swjt8c2JtOUArxHd1jyUTDA78xxOBHnelzf8GH7K7+/9Anbtplrr2piRkqoSskNn8Z4cgLuPrW99f7nMuZUSQBEqFf7RhL/ILW9Uech3qHIsVXN4cnYRcqiT3JNXaWeOvio1XjtryzqgVgwsb80pJt3t0Oce+Z7oUZRQM8eaJJ0Yj05ceLNex15H9RcAx8ukVBrEzo7NYqRIt9PPihPhxVoP5lSVWL1leyIrxKVgPt6Rkp190OJyNYI4LJlfvQcwfvoYhNFRONcorxFkRdBF4+wb+KkoHruF';

        $resp = urldecode($resp);

        $p = $this->parseResponse1($resp,$this->resSignFields,$this->merchantPrivateKeyPath,$this->merchantPrivateKeyPwd,$this->easyPublicKey);

       // var_dump($p);exit();

        if(!$p || empty($p)){
            exit("wrong");
        }

        //获取订单信息
        $orderid = $p['merchantOrderNo'];

        //获取订单
        $find = $this->checkOrderNotify($orderid);
        if(!$find){
            echo 'order_not_found'; exit;
        }

        //保存回调数据
        M("Order")->where(array("pay_orderid"=>$orderid))->save(array(
             "pay_orderid_rd"=>htmlspecialchars($p['orderNo'])
        ));


        $real_amount =  $p['amount'];     //实际支付金额
        $pay_amount =  $find['pay_amount']; //商户提交订单金额

        if(abs($real_amount - $pay_amount) > 1){
            echo 'wrong_money'; exit;
        }

        //获取渠道信息
        $this->config = Channel::getInstance()->find(['code'=>$this->class]);


        if(!$this->verify1($p, $this->resSignFields, $p['signMsg'], $this->easyPublicKey)) {
            exit("sign_wrong");
        }


        if($p['responseCode'] !== "0000" || $p['orderState'] !== "03" ){
            exit("sign_wrong");
        }


        $this->async($find['pay_orderid'],"success");
    }

    public function callBackUrl(){

        echo '处理成功'; exit;
    }
}
